using ERP.Domain.Warehouse.Allocation;
using Repository.communal;
using SqlSugar;
using Microsoft.EntityFrameworkCore;
using Warehouse.Dto;
using Warehouse.Repository;
using Warehouse.Services.IServices;

namespace Warehouse.Services.Services
{
    /// <summary>
    /// 调拨单服务实现
    /// </summary>
    public class AllocationService : IAllocationService
    {
        private readonly IAllocationRepository _allocationRepository;
        private readonly MyDbContext _context;
        private readonly ISqlSugarClient _db;

        public AllocationService(
            IAllocationRepository allocationRepository,
            MyDbContext context,
            ISqlSugarClient db)
        {
            _allocationRepository = allocationRepository;
            _context = context;
            _db = db;
        }

        public async Task<CreateAllocationResponse> CreateAllocationAsync(CreateAllocationRequest request)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                // 1. 验证调出仓库和调入仓库不能相同
                if (request.FromWarehouseId == request.ToWarehouseId)
                {
                    throw new ArgumentException("调出仓库和调入仓库不能相同");
                }

                // // 2. 如果不跳过验证，进行库存检查
                // if (!request.SkipValidation)
                // {
                //     foreach (var line in request.Lines)
                //     {
                //         var hasStock = await CheckStockAsync(line.ProductId, request.FromWarehouseId, line.FromLocationId, line.ReqQty);
                //         if (!hasStock)
                //         {
                //             throw new InvalidOperationException($"商品 {line.ProductName} 在调出仓库库存不足");
                //         }
                //     }
                // }

                // 3. 生成调拨单号
                var transferNo = await _allocationRepository.GetNextTransferNoAsync();

                // 4. 创建调拨单头
                var transferOrder = new TransferOrder
                {
                    TransferNo = transferNo,
                    BillStatus = 20, // 已审核（跳过审核，直接允许出入库）
                    ApplyDate = request.ApplyDate,
                    TransferType = request.TransferType,
                    FromWarehouseId = request.FromWarehouseId,
                    ToWarehouseId = request.ToWarehouseId,
                    OutStatus = 0, // 未出库
                    InStatus = 0,  // 未入库
                    TotalQty = request.Lines.Sum(x => x.ReqQty),
                    TotalAmount = request.Lines.Sum(x => x.ReqQty * x.UnitPrice),
                    HandlerName = request.HandlerName,
                    ApplicantName = request.ApplicantName,
                    Remark = request.Remark,
                    CreatedBy = "System", // 这里应该从当前用户获取
                    CreatedAt = DateTime.Now,
                    UpdatedAt = DateTime.Now
                };

                await _allocationRepository.AddAsync(transferOrder);
                await _context.SaveChangesAsync();

                // 5. 创建调拨单明细
                var lines = new List<TransferOrderLine>();
                for (int i = 0; i < request.Lines.Count; i++)
                {
                    var lineDto = request.Lines[i];
                    var line = new TransferOrderLine
                    {
                        TransferId = transferOrder.Id,
                        LineNo = i + 1,
                        ProductId = lineDto.ProductId,
                        ProductName = lineDto.ProductName,
                        ProductNo = lineDto.ProductNo,
                        Specification = lineDto.Specification,
                        Unit = lineDto.Unit,
                        FromLocationId = lineDto.FromLocationId,
                        ToLocationId = lineDto.ToLocationId,
                        ReqQty = lineDto.ReqQty,
                        OutQty = 0,
                        InQty = 0,
                        UnitPrice = lineDto.UnitPrice,
                        CreatedAt = DateTime.Now,
                        UpdatedAt = DateTime.Now
                    };
                    lines.Add(line);
                }

                await _allocationRepository.InsertLinesAsync(lines);

                // 6. 记录操作日志
                var opLog = new TransferOpLog
                {
                    TransferId = transferOrder.Id,
                    OpType = "CREATE",
                    OpContent = $"创建调拨单，调拨单号：{transferNo}",
                    OpUser = "System", // 这里应该从当前用户获取
                    CreatedAt = DateTime.Now
                };

                await _db.Insertable(opLog).ExecuteCommandAsync();

                await transaction.CommitAsync();

                return new CreateAllocationResponse
                {
                    TransferId = transferOrder.Id,
                    TransferNo = transferOrder.TransferNo,
                    Message = "调拨单创建成功"
                };
            }
            catch
            {
                await transaction.RollbackAsync();
                throw;
            }
        }

        public async Task<List<AllocationQueryDto>> GetTransferListAsync(int pageIndex = 1, int pageSize = 20)
        {
            var query = await _db.Queryable<TransferOrder>()
                .OrderByDescending(x => x.CreatedAt)
                .Skip((pageIndex - 1) * pageSize)
                .Take(pageSize)
                .ToListAsync();

            return query.Select(x => new AllocationQueryDto
            {
                Id = x.Id,
                TransferNo = x.TransferNo,
                BillStatus = x.BillStatus,
                ApplyDate = x.ApplyDate,
                TransferType = x.TransferType,
                FromWarehouseId = x.FromWarehouseId,
                ToWarehouseId = x.ToWarehouseId,
                FromWarehouseName = $"仓库{x.FromWarehouseId}", // 这里应该查询仓库名称
                ToWarehouseName = $"仓库{x.ToWarehouseId}", // 这里应该查询仓库名称
                TotalQty = x.TotalQty,
                TotalAmount = x.TotalAmount,
                HandlerName = x.HandlerName ?? "",
                ApplicantName = x.ApplicantName ?? "",
                CreatedAt = x.CreatedAt
            }).ToList();
        }

        public async Task<AllocationQueryDto?> GetTransferDetailAsync(int transferId)
        {
            var transfer = await _allocationRepository.GetModelAsync(transferId);
            if (transfer == null) return null;

            return new AllocationQueryDto
            {
                Id = transfer.Id,
                TransferNo = transfer.TransferNo,
                BillStatus = transfer.BillStatus,
                ApplyDate = transfer.ApplyDate,
                TransferType = transfer.TransferType,
                FromWarehouseId = transfer.FromWarehouseId,
                ToWarehouseId = transfer.ToWarehouseId,
                FromWarehouseName = $"仓库{transfer.FromWarehouseId}",
                ToWarehouseName = $"仓库{transfer.ToWarehouseId}",
                TotalQty = transfer.TotalQty,
                TotalAmount = transfer.TotalAmount,
                HandlerName = transfer.HandlerName ?? "",
                ApplicantName = transfer.ApplicantName ?? "",
                CreatedAt = transfer.CreatedAt
            };
        }

        public async Task<List<AllocationLineQueryDto>> GetTransferLinesAsync(int transferId)
        {
            var lines = await _allocationRepository.GetLinesAsync(transferId);
            return lines.Select(x => new AllocationLineQueryDto
            {
                Id = x.Id,
                TransferId = x.TransferId,
                LineNo = x.LineNo,
                ProductId = x.ProductId,
                ProductName = x.ProductName,
                ProductNo = x.ProductNo,
                Specification = x.Specification,
                Unit = x.Unit,
                FromLocationId = x.FromLocationId,
                ToLocationId = x.ToLocationId,
                FromLocationName = $"库位{x.FromLocationId}",
                ToLocationName = $"库位{x.ToLocationId}",
                ReqQty = x.ReqQty,
                OutQty = x.OutQty,
                InQty = x.InQty,
                UnitPrice = x.UnitPrice
            }).ToList();
        }

        public async Task<bool> CheckStockAsync(int productId, int warehouseId, int? locationId, decimal requiredQty)
        {
            var stock = await GetProductStockAsync(productId, warehouseId, locationId);
            return stock >= requiredQty;
        }

        public async Task<decimal> GetProductStockAsync(int productId, int warehouseId, int? locationId = null)
        {
            var query = _context.Inventory
                .Where(x => x.ProductId == productId && x.WarehouseId == warehouseId)
                .AsQueryable();

            if (locationId.HasValue)
            {
                query = query.Where(x => x.LocationId == locationId.Value);
            }

            var inventory = await query.FirstOrDefaultAsync();
            return inventory?.Quantity ?? 0m;
        }

        public async Task<TransferOutResponse> TransferOutAsync(TransferOutRequest request)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                // 1. 验证调拨单是否存在且状态正确
                var transfer = await _allocationRepository.GetModelAsync(request.TransferId);
                if (transfer == null)
                {
                    throw new ArgumentException("调拨单不存在");
                }

                // 跳过审核检查，调拨单创建后直接允许出库

                if (transfer.OutStatus == 2) // 假设2为完全出库状态
                {
                    throw new InvalidOperationException("调拨单已完全出库");
                }

                // 2. 验证出库数量和库存
                var lines = await _allocationRepository.GetLinesAsync(request.TransferId);
                var outRecords = new List<TransferOutRecord>();

                foreach (var requestLine in request.Lines)
                {
                    var line = lines.FirstOrDefault(x => x.Id == requestLine.LineId);
                    if (line == null)
                    {
                        throw new ArgumentException($"调拨单明细不存在，LineId: {requestLine.LineId}");
                    }

                    // 检查剩余可出库数量
                    var remainingQty = line.ReqQty - line.OutQty;
                    if (requestLine.Qty > remainingQty)
                    {
                        throw new InvalidOperationException($"商品 {line.ProductName} 出库数量超过剩余可出库数量");
                    }

                    // 跳过库存检查，直接进行出库操作

                    // 3. 创建出库记录
                    var outRecord = new TransferOutRecord
                    {
                        TransferId = request.TransferId,
                        LineId = requestLine.LineId,
                        ProductId = requestLine.ProductId,
                        WarehouseId = requestLine.WarehouseId,
                        LocationId = requestLine.LocationId,
                        Qty = requestLine.Qty,
                        UnitPrice = requestLine.UnitPrice,
                        Amount = requestLine.Qty * requestLine.UnitPrice,
                        OperatorName = request.OperatorName,
                        OperatedAt = DateTime.Now,
                        Remark = request.Remark
                    };
                    outRecords.Add(outRecord);

                    // 4. 更新调拨单明细出库数量（EF 保证与当前事务一致）
                    var lineEntity = await _context.TransferOrderLine.FirstAsync(x => x.Id == requestLine.LineId);
                    lineEntity.OutQty = lineEntity.OutQty + requestLine.Qty;
                    lineEntity.UpdatedAt = DateTime.Now;

                    // 5. 更新库存（减少）
                    await UpdateInventoryAsync(requestLine.ProductId, requestLine.WarehouseId, requestLine.LocationId, -requestLine.Qty, requestLine.UnitPrice);

                    // 6. 记录库存交易
                    await RecordInventoryTransactionAsync(requestLine.ProductId, line.ProductName, line.ProductNo, 
                        line.Specification, line.Unit, requestLine.WarehouseId, requestLine.LocationId, 
                        "调拨出库", -requestLine.Qty, requestLine.UnitPrice, request.OperatorName, 
                        $"调拨单出库：{transfer.TransferNo}");
                }

                // 7. 批量插入出库记录
                await _context.TransferOutRecord.AddRangeAsync(outRecords);

                // 8. 检查是否完全出库，更新调拨单状态
                var updatedLines = await _context.TransferOrderLine
                    .Where(x => x.TransferId == request.TransferId)
                    .ToListAsync();
                var isFullyOut = updatedLines.All(x => x.OutQty >= x.ReqQty);
                var outStatus = isFullyOut ? (byte)2 : (byte)1; // 1=部分出库，2=完全出库

                await _allocationRepository.UpdateTransferStatusAsync(request.TransferId, outStatus: outStatus);

                // 9. 记录操作日志
                var opLog = new TransferOpLog
                {
                    TransferId = request.TransferId,
                    OpType = "OUT",
                    OpContent = $"调拨出库，出库数量：{request.Lines.Sum(x => x.Qty)}",
                    OpUser = request.OperatorName,
                    CreatedAt = DateTime.Now
                };
                await _context.TransferOpLog.AddAsync(opLog);

                await _context.SaveChangesAsync();

                await transaction.CommitAsync();

                return new TransferOutResponse
                {
                    TransferId = request.TransferId,
                    TransferNo = transfer.TransferNo,
                    Message = "调拨出库成功",
                    OutRecords = outRecords.Select(x => new TransferOutRecordDto
                    {
                        Id = x.Id,
                        TransferId = x.TransferId,
                        LineId = x.LineId,
                        ProductId = x.ProductId,
                        WarehouseId = x.WarehouseId,
                        LocationId = x.LocationId,
                        Qty = x.Qty,
                        UnitPrice = x.UnitPrice,
                        Amount = x.Amount,
                        OperatorName = x.OperatorName,
                        OperatedAt = x.OperatedAt,
                        Remark = x.Remark
                    }).ToList()
                };
            }
            catch
            {
                await transaction.RollbackAsync();
                throw;
            }
        }

        public async Task<TransferInResponse> TransferInAsync(TransferInRequest request)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                // 1. 验证调拨单是否存在且状态正确
                var transfer = await _allocationRepository.GetModelAsync(request.TransferId);
                if (transfer == null)
                {
                    throw new ArgumentException("调拨单不存在");
                }

                if (transfer.OutStatus == 0) // 假设0为未出库状态
                {
                    throw new InvalidOperationException("调拨单尚未出库，无法入库");
                }

                if (transfer.InStatus == 2) // 假设2为完全入库状态
                {
                    throw new InvalidOperationException("调拨单已完全入库");
                }

                // 2. 验证入库数量
                var lines = await _allocationRepository.GetLinesAsync(request.TransferId);
                var inRecords = new List<TransferInRecord>();

                foreach (var requestLine in request.Lines)
                {
                    var line = lines.FirstOrDefault(x => x.Id == requestLine.LineId);
                    if (line == null)
                    {
                        throw new ArgumentException($"调拨单明细不存在，LineId: {requestLine.LineId}");
                    }

                    // 检查剩余可入库数量
                    var remainingQty = line.OutQty - line.InQty;
                    if (requestLine.Qty > remainingQty)
                    {
                        throw new InvalidOperationException($"商品 {line.ProductName} 入库数量超过剩余可入库数量");
                    }

                    // 3. 创建入库记录
                    var inRecord = new TransferInRecord
                    {
                        TransferId = request.TransferId,
                        LineId = requestLine.LineId,
                        ProductId = requestLine.ProductId,
                        WarehouseId = requestLine.WarehouseId,
                        LocationId = requestLine.LocationId,
                        Qty = requestLine.Qty,
                        UnitPrice = requestLine.UnitPrice,
                        Amount = requestLine.Qty * requestLine.UnitPrice,
                        OperatorName = request.OperatorName,
                        OperatedAt = DateTime.Now,
                        Remark = request.Remark
                    };
                    inRecords.Add(inRecord);

                    // 4. 更新调拨单明细入库数量
                    var lineEntity = await _context.TransferOrderLine.FirstAsync(x => x.Id == requestLine.LineId);
                    lineEntity.InQty = lineEntity.InQty + requestLine.Qty;
                    lineEntity.UpdatedAt = DateTime.Now;

                    // 5. 更新库存（增加）
                    await UpdateInventoryAsync(requestLine.ProductId, requestLine.WarehouseId, requestLine.LocationId, requestLine.Qty, requestLine.UnitPrice);

                    // 6. 记录库存交易
                    await RecordInventoryTransactionAsync(requestLine.ProductId, line.ProductName, line.ProductNo, 
                        line.Specification, line.Unit, requestLine.WarehouseId, requestLine.LocationId, 
                        "调拨入库", requestLine.Qty, requestLine.UnitPrice, request.OperatorName, 
                        $"调拨单入库：{transfer.TransferNo}");
                }

                // 7. 批量插入入库记录
                await _context.TransferInRecord.AddRangeAsync(inRecords);

                // 8. 检查是否完全入库，更新调拨单状态
                var updatedLines = await _context.TransferOrderLine
                    .Where(x => x.TransferId == request.TransferId)
                    .ToListAsync();
                var isFullyIn = updatedLines.All(x => x.InQty >= x.OutQty);
                var inStatus = isFullyIn ? (byte)2 : (byte)1; // 1=部分入库，2=完全入库

                // 如果完全入库，同时更新单据状态为已完成
                var billStatus = isFullyIn ? (byte)30 : (byte?)null; // 假设30为已完成状态

                await _allocationRepository.UpdateTransferStatusAsync(request.TransferId, billStatus: billStatus, inStatus: inStatus);

                // 9. 记录操作日志
                var opLog = new TransferOpLog
                {
                    TransferId = request.TransferId,
                    OpType = "IN",
                    OpContent = $"调拨入库，入库数量：{request.Lines.Sum(x => x.Qty)}",
                    OpUser = request.OperatorName,
                    CreatedAt = DateTime.Now
                };
                await _context.TransferOpLog.AddAsync(opLog);

                await _context.SaveChangesAsync();

                await transaction.CommitAsync();

                return new TransferInResponse
                {
                    TransferId = request.TransferId,
                    TransferNo = transfer.TransferNo,
                    Message = "调拨入库成功",
                    InRecords = inRecords.Select(x => new TransferInRecordDto
                    {
                        Id = x.Id,
                        TransferId = x.TransferId,
                        LineId = x.LineId,
                        ProductId = x.ProductId,
                        WarehouseId = x.WarehouseId,
                        LocationId = x.LocationId,
                        Qty = x.Qty,
                        UnitPrice = x.UnitPrice,
                        Amount = x.Amount,
                        OperatorName = x.OperatorName,
                        OperatedAt = x.OperatedAt,
                        Remark = x.Remark
                    }).ToList()
                };
            }
            catch
            {
                await transaction.RollbackAsync();
                throw;
            }
        }

        public async Task<List<TransferOutRecordDto>> GetTransferOutRecordsAsync(int transferId)
        {
            var records = await _allocationRepository.GetOutRecordsAsync(transferId);
            var lines = await _context.TransferOrderLine
                .Where(l => l.TransferId == transferId)
                .ToListAsync();

            return records.Select(x =>
            {
                var line = lines.FirstOrDefault(l => l.Id == x.LineId);
                return new TransferOutRecordDto
            {
                Id = x.Id,
                TransferId = x.TransferId,
                LineId = x.LineId,
                ProductId = x.ProductId,
                    ProductName = line?.ProductName ?? string.Empty,
                    ProductNo = line?.ProductNo ?? string.Empty,
                WarehouseId = x.WarehouseId,
                LocationId = x.LocationId,
                    WarehouseName = $"仓库{x.WarehouseId}",
                    LocationName = x.LocationId.HasValue ? $"库位{x.LocationId}" : string.Empty,
                Qty = x.Qty,
                UnitPrice = x.UnitPrice,
                Amount = x.Amount,
                OperatorName = x.OperatorName,
                OperatedAt = x.OperatedAt,
                Remark = x.Remark
                };
            }).ToList();
        }

        public async Task<List<TransferInRecordDto>> GetTransferInRecordsAsync(int transferId)
        {
            var records = await _allocationRepository.GetInRecordsAsync(transferId);
            var lines = await _context.TransferOrderLine
                .Where(l => l.TransferId == transferId)
                .ToListAsync();

            return records.Select(x =>
            {
                var line = lines.FirstOrDefault(l => l.Id == x.LineId);
                return new TransferInRecordDto
            {
                Id = x.Id,
                TransferId = x.TransferId,
                LineId = x.LineId,
                ProductId = x.ProductId,
                    ProductName = line?.ProductName ?? string.Empty,
                    ProductNo = line?.ProductNo ?? string.Empty,
                WarehouseId = x.WarehouseId,
                LocationId = x.LocationId,
                    WarehouseName = $"仓库{x.WarehouseId}",
                    LocationName = x.LocationId.HasValue ? $"库位{x.LocationId}" : string.Empty,
                Qty = x.Qty,
                UnitPrice = x.UnitPrice,
                Amount = x.Amount,
                OperatorName = x.OperatorName,
                OperatedAt = x.OperatedAt,
                Remark = x.Remark
                };
            }).ToList();
        }

        public async Task<List<TransferOpLogDto>> GetTransferOpLogsAsync(int transferId)
        {
            var query = _context.TransferOpLog
                .Where(x => x.TransferId == transferId)
                .OrderBy(x => x.CreatedAt);

            var list = await query.ToListAsync();
            return list.Select(x => new TransferOpLogDto
            {
                Id = x.Id,
                TransferId = x.TransferId,
                OpType = x.OpType,
                OpContent = x.OpContent ?? string.Empty,
                OpUser = x.OpUser ?? string.Empty,
                CreatedAt = x.CreatedAt
            }).ToList();
        }

        /// <summary>
        /// 更新库存
        /// </summary>
        private async Task UpdateInventoryAsync(int productId, int warehouseId, int? locationId, decimal qty, decimal unitPrice)
        {
            var inventory = await _context.Inventory
                .FirstOrDefaultAsync(x => x.ProductId == productId && x.WarehouseId == warehouseId && x.LocationId == (locationId ?? 0));

            if (inventory == null)
            {
                inventory = new ERP.Domain.Warehouse.Allocation.Inventory
                {
                    ProductId = productId,
                    WarehouseId = warehouseId,
                    LocationId = locationId ?? 0,
                    Quantity = 0,
                    LockedQty = 0,
                    AvgPrice = unitPrice,
                    UpdatedAt = DateTime.Now
                };
                await _context.Inventory.AddAsync(inventory);
            }

            inventory.Quantity += qty;
            if (inventory.Quantity > 0)
            {
                var totalAmount = inventory.Quantity * inventory.AvgPrice + qty * unitPrice;
                inventory.AvgPrice = totalAmount / inventory.Quantity;
            }
            inventory.UpdatedAt = DateTime.Now;
        }

        /// <summary>
        /// 记录库存交易
        /// </summary>
        private async Task RecordInventoryTransactionAsync(int productId, string productName, string productNo, 
            string specification, string unit, int warehouseId, int? locationId, string transactionType, 
            decimal qty, decimal unitPrice, string operatorName, string remark)
        {
            var transaction = new ERP.Domain.Warehouse.InventoryTransaction
            {
                ProductName = productName,
                ProductNumber = productNo,
                Specification = specification,
                Unit = unit,
                WarehouseName = $"仓库{warehouseId}",
                WarehouseAreaName = "",
                BinLocationName = locationId.HasValue ? $"库位{locationId}" : "",
                TransactionType = transactionType,
                Quantity = qty,
                UnitPrice = unitPrice,
                TransactionTime = DateTime.Now,
                Applicant = operatorName,
                Remarks = remark
            };

            await _context.InventoryTransaction.AddAsync(transaction);
        }

    }
}
